;>*******************************************************************************************************************************
;*																																*
;*	File:	CortexSaver.asm																										*
;*																																*
;*	This is the main file of the Code Cortex screen saver.																		*
;*																																*
;*	Compiling:																													*
;*	Assemble	- "$MASM$\bin\ML.EXE" /c /coff /Cp /nologo /I"$MASM$\include" $this$											*
;*	Link		- "$MASM$\bin\LINK.EXE" /SUBSYSTEM:WINDOWS /RELEASE /VERSION:4.0 /LIBPATH:"$MASM$\lib" /OUT:"CortexSaver.scr"	*
;*	CortexSaver.obj																												*
;*																																*
;*	Libraries:																													*
;*	kernel32	- This is for general system calls.																				*
;*	gdi32		- This is for graphics system calls.																			*
;*	user32		- This is for windowing system calls																			*
;*																																*
;*	Imports:																													*
;*	windows.inc	- This defines constants and structures needed for making a Windows program.									*
;*																																*
;<*******************************************************************************************************************************

.686p
.xmm
.model flat,stdcall

include	kernel32.inc
include	gdi32.inc
include	user32.inc

includelib	kernel32.lib
includelib	gdi32.lib
includelib	user32.lib

include		windows.inc

include	CortexSaver.inc
include	Random.asm
include	Render.asm
include	Neuron.asm

ProcessCommandLine		proto
GenerationThread		proto	lpParameter:DWORD

;>*******************************************************************************************************************************
;*																																*
;*	Section:	Data																											*
;*																																*
;*																																*
;<*******************************************************************************************************************************

.data

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	MouseMovePixels																									*
;*																																*
;*	Counts down to zero, then program ends once negative																		*
;*																																*
;<*******************************************************************************************************************************
align	4
MouseMovePixels	DWORD	8

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	pImageBuffer																									*
;*																																*
;*																																*
;<*******************************************************************************************************************************
align	4
pImageBuffer	DWORD	?

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	CursorX																											*
;*																																*
;*																																*
;<*******************************************************************************************************************************
align	4
CursorX	DWORD	?

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	CursorY																											*
;*																																*
;*																																*
;<*******************************************************************************************************************************
align	4
CursorY	DWORD	?

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	BitmapInfo																										*
;*																																*
;*																																*
;<*******************************************************************************************************************************
align	4
BitmapInfo	BITMAPINFO	<<sizeof BITMAPINFOHEADER,IMAGE_WIDTH,-IMAGE_HEIGHT,1,32,BI_RGB,0,0,0,0,0>,<0,0,0,0>>

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	WindowBounds																									*
;*																																*
;*																																*
;<*******************************************************************************************************************************
align	4
WindowBounds	RECT	<0,0,?,?>

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	WindowClass																										*
;*																																*
;*																																*
;<*******************************************************************************************************************************
align	4
WindowClass	WNDCLASSEX	<sizeof WNDCLASSEX,NULL,offset WindowProc,NULL,NULL,?,?,NULL,COLOR_WINDOWTEXT+1,NULL,offset WindowClassName,?>

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	hWndGlobal																										*
;*																																*
;*																																*
;<*******************************************************************************************************************************
align	4
hWndGlobal	DWORD	?

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	Message																											*
;*																																*
;*																																*
;<*******************************************************************************************************************************
align	4
Message	MSG	<>

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	isPreview																										*
;*																																*
;*																																*
;<*******************************************************************************************************************************
align	4
isPreview	BYTE	FALSE

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	isSSE3Supported																									*
;*																																*
;*																																*
;<*******************************************************************************************************************************
align	4
isSSE3Supported	BYTE	?

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	AppName																											*
;*																																*
;*																																*
;<*******************************************************************************************************************************
align	4
AppName	BYTE	"Code Cortex Screen Saver",0

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	WindowClassName																									*
;*																																*
;*																																*
;<*******************************************************************************************************************************
align	4
WindowClassName	BYTE	"FullPopupWindow",0

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	TextSSE2Message																									*
;*																																*
;*																																*
;<*******************************************************************************************************************************
align	4
TextSSE2Message	BYTE	"Sorry, this screen saver requires a processor that supports SSE2.",0

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	TextConfigMessage																								*
;*																																*
;*																																*
;<*******************************************************************************************************************************
align	4
TextConfigMessage	BYTE	"This program has no configuration options.",0

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	TextPasswordMessage																								*
;*																																*
;*																																*
;<*******************************************************************************************************************************
align	4
TextPasswordMessage	BYTE	"This program does not support this old method of password protection because it is not safe.",0

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	FontName																										*
;*																																*
;*																																*
;<*******************************************************************************************************************************
align	4
FontName	BYTE	"Verdana",0

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	hFont																											*
;*																																*
;*																																*
;<*******************************************************************************************************************************
align	4
hFont	DWORD	?

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	hFile																											*
;*																																*
;*	This is a file handle for whenever file I/O is used.																		*
;*																																*
;<*******************************************************************************************************************************
align	4
hFile	DWORD	?

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	pTimingData																										*
;*																																*
;*	When TIMING is TRUE, this holds the address of the block of memory used to hold timing data									*
;*																																*
;<*******************************************************************************************************************************
align	4
pTimingData	DWORD	0

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	nFrames																											*
;*																																*
;*	When TIMING is TRUE or SAVING_IMAGES is TRUE, this is the number of frames that have passed.								*
;*																																*
;<*******************************************************************************************************************************
align	4
nFrames	DWORD	0

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	SavedTime																										*
;*																																*
;*	This is where the previous recorded time is saved when TIMING is TRUE.														*
;*																																*
;<*******************************************************************************************************************************
align	8
SavedTime	QWORD	0

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	Dummy																											*
;*																																*
;*	This is a variable used to hold the number of bytes read from or written to a file.  Its contents are completely ignored.	*
;*																																*
;<*******************************************************************************************************************************
align	4
Dummy	DWORD	?

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	TimingFile																										*
;*																																*
;*	This is the name of the file to hold the timing data when TIMING is TRUE.													*
;*																																*
;<*******************************************************************************************************************************
align	4
TimingFile	BYTE	"Timing.dat",0

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	PointsFileName																									*
;*																																*
;*	This is the name of one of the files to hold the points data when SAVING_POINTS is TRUE.									*
;*																																*
;<*******************************************************************************************************************************
align	4
PointsFileName	BYTE	"PointsQ.dat",0

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	WeightsFileName																									*
;*																																*
;*	This is the name of one of the files to hold the weights data when SAVING_POINTS is TRUE.									*
;*																																*
;<*******************************************************************************************************************************
align	4
WeightsFileName	BYTE	"WeightsQ.dat",0

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	isDoneGenerating																								*
;*																																*
;*																																*
;<*******************************************************************************************************************************
align	4
isDoneGenerating	BYTE	FALSE

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	DecimalFormat																									*
;*																																*
;*																																*
;<*******************************************************************************************************************************
align	4
DecimalFormat	BYTE	"Generating the Cortex: %lu%% Complete",0

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	TextBuffer																										*
;*																																*
;*																																*
;<*******************************************************************************************************************************
align	4
TextBuffer	BYTE	64 dup (?)

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	SavedBitmapFileHeader																							*
;*																																*
;*																																*
;<*******************************************************************************************************************************
align	4
SavedBitmapFileHeader	BITMAPFILEHEADER	<"MB",sizeof BITMAPFILEHEADER + sizeof BITMAPINFOHEADER + (IMAGE_WIDTH/4)*(IMAGE_HEIGHT/4)*3,0,0,sizeof BITMAPFILEHEADER+sizeof BITMAPINFOHEADER>
						BITMAPINFOHEADER	<sizeof BITMAPINFOHEADER,IMAGE_WIDTH/4,IMAGE_HEIGHT/4,1,24,BI_RGB,0,0,0,0,0>

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	ImageFileName																									*
;*																																*
;*																																*
;<*******************************************************************************************************************************
align	4
ImageFileName	BYTE	"Image000.bmp",0


;>*******************************************************************************************************************************
;*																																*
;*	Section:	Code																											*
;*																																*
;*																																*
;<*******************************************************************************************************************************

.code

;>*******************************************************************************************************************************
;*																																*
;*	Function:	main																											*
;*																																*
;*	Execution starts here.																										*
;*																																*
;<*******************************************************************************************************************************
main	proc
	invoke	ProcessCommandLine
	mov		edx,offset TextConfigMessage
	cmp		eax,MODAL_CONFIG
	je		ModalConfig
	cmp		eax,MODELESS_CONFIG
	je		ModelessConfig
	mov		edx,offset TextPasswordMessage
	cmp		eax,SET_PASSWORD
	je		SetPassword

;At this point, either the program is known to be running as normal or running in the preview window.
;Check to make sure that the processor supports SSE3, so that the program can make use of instructions like haddps.
	mov		esi,eax
	mov		eax,1
	cpuid
	test	ecx,1
	setnz	isSSE3Supported
	test	edx,4000000h
	mov		edx,offset TextSSE2Message
	jz		ProcessorWithoutSSE2

;Now that it is confirmed that the program is going ahead, set up the window class
	invoke	GetModuleHandle,NULL
	mov		WindowClass.hInstance,eax
	invoke	LoadIcon,eax,IDI_LOGO
	mov		WindowClass.hIcon,eax
	mov		WindowClass.hIconSm,eax
	invoke	RegisterClassEx,offset WindowClass

;Allocate the necessary memory for rendering
	invoke	VirtualAlloc,NULL,IMAGE_WIDTH*IMAGE_HEIGHT*4,MEM_COMMIT,PAGE_READWRITE
	mov		pImageBuffer,eax
	invoke	VirtualAlloc,NULL,IMAGE_WIDTH*IMAGE_HEIGHT*2,MEM_COMMIT,PAGE_READWRITE
	mov		pIntermediateBuffer,eax
	
;Allocate the memory for the neuron data
	invoke	VirtualAlloc,NULL,NUM_TOTAL_POINTS*10h,MEM_COMMIT,PAGE_READWRITE
	mov		pPoints,eax
	invoke	VirtualAlloc,NULL,NUM_TOTAL_POINTS*10h,MEM_COMMIT,PAGE_READWRITE
	mov		pPointsQ,eax
	invoke	VirtualAlloc,NULL,NUM_TOTAL_POINTS*10h,MEM_COMMIT,PAGE_READWRITE
	mov		pPointsR,eax
	invoke	VirtualAlloc,NULL,NUM_TOTAL_POINTS*10h,MEM_COMMIT,PAGE_READWRITE
	mov		pPointsS,eax
	invoke	VirtualAlloc,NULL,NUM_TOTAL_POINTS*10h,MEM_COMMIT,PAGE_READWRITE
	mov		pScreenPoints,eax
	invoke	VirtualAlloc,NULL,NUM_TOTAL_POINTS*4,MEM_COMMIT,PAGE_READWRITE
	mov		pWeights,eax
	invoke	VirtualAlloc,NULL,NUM_TOTAL_POINTS*4,MEM_COMMIT,PAGE_READWRITE
	mov		pWeightsQ,eax
	invoke	VirtualAlloc,NULL,NUM_TOTAL_POINTS*4,MEM_COMMIT,PAGE_READWRITE
	mov		pWeightsR,eax
	invoke	VirtualAlloc,NULL,NUM_TOTAL_POINTS*4,MEM_COMMIT,PAGE_READWRITE
	mov		pWeightsS,eax

;Finally, check whether making a new full-screen window, or a window whose parent is the preview window.
	cmp		esi,RUN_NORMALLY
	jne		PreviewWindow
NormalWindow:
	invoke	GetSystemMetrics,SM_CXSCREEN
	mov		WindowBounds.right,eax
	invoke	GetSystemMetrics,SM_CYSCREEN
	mov		WindowBounds.bottom,eax
	invoke	CreateWindowEx,WS_EX_TOPMOST,offset WindowClassName,offset AppName,WS_POPUP or WS_VISIBLE or WS_MAXIMIZE,0,0,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,WindowClass.hInstance,NULL
	mov		hWndGlobal,eax

	mov		eax,WindowBounds.right
	mov		ecx,WindowBounds.bottom
	shr		eax,1
	shr		ecx,1
	mov		CursorX,eax
	mov		CursorY,ecx
	invoke	SetCursorPos,eax,ecx	;put cursor in middle of screen
	invoke	ShowCursor,FALSE

	jmp		MessageLoop

PreviewWindow:
	mov		isPreview,TRUE
	invoke	GetClientRect,esi,offset WindowBounds
	mov		eax,WindowBounds.right
	mov		edx,WindowBounds.bottom
	sub		eax,WindowBounds.left
	sub		edx,WindowBounds.top
	mov		WindowBounds.right,eax
	mov		WindowBounds.bottom,edx
	invoke	CreateWindowEx,NULL,offset WindowClassName,offset AppName,WS_CHILD or WS_VISIBLE,WindowBounds.left,WindowBounds.top,eax,edx,esi,NULL,WindowClass.hInstance,NULL
	mov		hWndGlobal,eax

MessageLoop:
	invoke	GetMessage,offset Message,NULL,0,0
	test	eax,eax
	jz		DoneMessageLoop
	invoke	DispatchMessage,offset Message
	jmp		MessageLoop
	


SetPassword:
ModalConfig:
ModelessConfig:
ProcessorWithoutSSE2:
	invoke	MessageBox,NULL,edx,offset AppName,MB_OK
DoneMessageLoop:
	invoke	ExitProcess,0

main	endp

;>*******************************************************************************************************************************
;*																																*
;*	Function:	WindowProc																										*
;*																																*
;*																																*
;*	Parameters:																													*
;*	hWnd	-																													*
;*	uMsg	-																													*
;*	wParam	-																													*
;*	lParam	-																													*
;*																																*
;*	Local Variables:																											*
;*	ps	-																														*
;*	hDC	-																														*
;*																																*
;<*******************************************************************************************************************************
WindowProc	proc	hWnd:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
 LOCAL	hDC:HDC
 LOCAL	ps:PAINTSTRUCT
	push	ebx
	mov		eax,uMsg


	cmp		eax,WM_CREATE
	jne		NotWM_CREATE
	invoke	SetTimer,hWnd,REDRAW_TIMER_ID,LOADING_REDRAW_PERIOD,NULL
IF TIMING
	invoke	LocalAlloc,LMEM_FIXED,NUM_TIMING_FRAMES*TIME_SAMPLES_PER_FRAME*4
	mov		pTimingData,eax
ENDIF
	invoke	LocalAlloc,LMEM_FIXED,sizeof RANDOM
	mov		pRenderRandom,eax
	invoke	SetRandomSeed,pRenderRandom,123456

;Set the initial value for GoalQ and GoalR
	invoke	GetRandomFloat,pRenderRandom
	movss	xmm1,xmm0				;xmm1 = new GoalR
	invoke	GetRandomFloat,pRenderRandom
	movss	xmm2,xmm0				;xmm2 = xmm0 = new GoalQ
	addss	xmm2,xmm1				;xmm2 = new GoalQ + new GoalR
	comiss	xmm2,AllOne.x			;S=1-Q-R, so to ensure S>=0, it must be that Q+R<=1
	jbe		SIsNotNegative
	subss	xmm0,AllOne.x			;To do the rotation adjustment, simply do
	subss	xmm1,AllOne.x			;Q = 1-Q
	mulss	xmm0,AllNegativeOne.x	;R = 1-R
	mulss	xmm1,AllNegativeOne.x	;
SIsNotNegative:
	movss	GoalQ,xmm0
	movss	GoalR,xmm1

	mov		eax,32
	mov		ecx,14
	cmp		isPreview,TRUE
	jne		NotPreviewFontSize
	mov		eax,10
	mov		ecx,4
NotPreviewFontSize:
	invoke	CreateFont,eax,ecx,0,0,FW_BOLD,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH or FF_DONTCARE,offset FontName
	mov		hFont,eax

	invoke	CreateThread,NULL,20000h,offset GenerationThread,NULL,0,NULL
	jmp		Done
NotWM_CREATE:


	cmp		eax,WM_PAINT
	jne		NotWM_PAINT

	cmp		isDoneGenerating,TRUE
	je		DoneLoadingScreen

;Paint the Loading Screen
	mov		eax,nPointsGenerated
	imul	eax,eax,100
	add		eax,NUM_TOTAL_POINTS*3/2
	xor		edx,edx
	mov		ecx,NUM_TOTAL_POINTS*3
	div		ecx
	invoke	wsprintf,offset TextBuffer,offset DecimalFormat,eax
	push	eax
	invoke	BeginPaint,hWnd,addr ps
	mov		hDC,eax
	invoke	SelectObject,hDC,hFont
	invoke	SetTextColor,hDC,0FFA080h
	invoke	SetBkColor,hDC,000000h
	pop		ecx
	invoke	DrawText,hDC,offset TextBuffer,ecx,offset WindowBounds,DT_CENTER or DT_VCENTER or DT_SINGLELINE or DT_NOCLIP or DT_HIDEPREFIX
	invoke	EndPaint,hWnd,addr ps
	jmp		Done

DoneLoadingScreen:
IF TIMING
	mov		ecx,TIME_SAMPLES_PER_FRAME-1
	call	RecordTime
ENDIF

	call	UpdateObjects

IF TIMING
	xor		ecx,ecx
	call	RecordTime
ENDIF

	call	RenderImage

IF TIMING
	mov		ecx,TIME_SAMPLES_PER_FRAME-3
	call	RecordTime
ENDIF

	invoke	BeginPaint,hWnd,addr ps
;	mov		hDC,eax
;	invoke	SetDIBitsToDevice,eax,0,0,IMAGE_WIDTH,IMAGE_HEIGHT,0,0,0,IMAGE_HEIGHT,pImageBuffer,offset BitmapInfo,DIB_RGB_COLORS
	invoke	StretchDIBits,eax,0,0,WindowBounds.right,WindowBounds.bottom,0,0,IMAGE_WIDTH,IMAGE_HEIGHT,pImageBuffer,offset BitmapInfo,DIB_RGB_COLORS,SRCCOPY
	invoke	EndPaint,hWnd,addr ps

IF SAVING_IMAGES
	cmp		nFrames,1000
	je		DoneSavingImage
	call	SaveImage
	add		nFrames,1
DoneSavingImage:
ENDIF
IF TIMING
	mov		ecx,TIME_SAMPLES_PER_FRAME-2
	call	RecordTime
	invoke	InvalidateRect,hWnd,NULL,FALSE
	invoke	UpdateWindow,hWnd
ENDIF

	jmp		Done
NotWM_PAINT:


	cmp		eax,WM_TIMER
	jne		NotWM_TIMER
	invoke	InvalidateRect,hWnd,NULL,FALSE
	invoke	UpdateWindow,hWnd
	jmp		Done
NotWM_TIMER:


	cmp		eax,WM_DESTROY
	jne		NotWM_DESTROY
	invoke	KillTimer,hWnd,REDRAW_TIMER_ID
IF TIMING
	invoke	CreateFile,offset TimingFile,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_ARCHIVE,NULL
	mov		hFile,eax
	mov		eax,nFrames
IF TIME_SAMPLES_PER_FRAME NE 8
	.err <Change the following line for WM_DESTROY now that the size of all time samples in a frame is not 32 bytes.>
ENDIF
	shl		eax,5
	invoke	WriteFile,hFile,pTimingData,eax,offset Dummy,NULL
	invoke	CloseHandle,hFile
	invoke	LocalFree,pTimingData
ENDIF
	invoke	LocalFree,pRenderRandom
	invoke	VirtualFree,pImageBuffer,0,MEM_RELEASE
	invoke	VirtualFree,pIntermediateBuffer,0,MEM_RELEASE
	invoke	VirtualFree,pPoints,0,MEM_RELEASE
	invoke	VirtualFree,pPointsQ,0,MEM_RELEASE
	invoke	VirtualFree,pPointsR,0,MEM_RELEASE
	invoke	VirtualFree,pPointsS,0,MEM_RELEASE
	invoke	VirtualFree,pWeights,0,MEM_RELEASE
	invoke	VirtualFree,pWeightsQ,0,MEM_RELEASE
	invoke	VirtualFree,pWeightsR,0,MEM_RELEASE
	invoke	VirtualFree,pWeightsS,0,MEM_RELEASE
	invoke	VirtualFree,pScreenPoints,0,MEM_RELEASE
	invoke	DeleteObject,hFont
	invoke	PostQuitMessage,NULL
	jmp		Done
NotWM_DESTROY:

	cmp		isPreview,FALSE
	jne		SkipMessagesForNonPreview
	cmp		eax,WM_MOUSEMOVE
	jne		NotWM_MOUSEMOVE
	mov		eax,lParam
	mov		ecx,eax
	sar		ecx,16
	movsx	eax,ax
	sub		eax,CursorX
	sub		ecx,CursorY
	add		CursorX,eax
	add		CursorY,ecx
	test	eax,eax
	jns		PositiveX
	neg		eax
PositiveX:
	test	ecx,ecx
	jns		PositiveY
	neg		ecx
PositiveY:
	sub		MouseMovePixels,eax
	sub		MouseMovePixels,ecx
	js		Quit
	jmp		Done
NotWM_MOUSEMOVE:

	cmp		eax,WM_KEYDOWN
	je		Quit
	cmp		eax,WM_LBUTTONDOWN
	je		Quit
	cmp		eax,WM_MBUTTONDOWN
	je		Quit
	cmp		eax,WM_RBUTTONDOWN
	je		Quit


	cmp		eax,WM_SYSCOMMAND
	jne		NotWM_SYSCOMMAND
	cmp		wParam,SC_SCREENSAVE
	je		Done	;Handle the SC_SCREENSAVE notification to stop the screensaver from starting again
NotWM_SYSCOMMAND:

SkipMessagesForNonPreview:

;Default action:
	invoke	DefWindowProc,hWnd,uMsg,wParam,lParam
	pop		ebx
	ret

Quit:
	invoke	SendMessage,hWnd,WM_DESTROY,0,0

Done:
	xor		eax,eax
	pop		ebx
	ret
WindowProc	endp

;>*******************************************************************************************************************************
;*																																*
;*	Function:	GenerationThread																								*
;*																																*
;*	This thread generates the neurons.																							*
;*																																*
;*	Parameters:																													*
;*	lpParameter	-																												*
;*																																*
;<*******************************************************************************************************************************
GenerationThread	proc	lpParameter:DWORD
	mov		ebx,pPointsQ
	mov		edi,pWeightsQ
	invoke	GenerateNeurons,NUM_CUBE_NEURONS,offset NeuronCentrePoints,123,1234567,ebx,edi,offset NeuronGraph
	add		ebx,NUM_CUBE_POINTS*sizeof VECTOR
	add		edi,NUM_CUBE_POINTS*4
	invoke	GenerateNeurons,1,offset AllZero,1234,123456,ebx,edi,NULL
	add		ebx,NUM_ORBITA_POINTS*sizeof VECTOR
	add		edi,NUM_ORBITA_POINTS*4
	invoke	GenerateNeurons,1,offset AllZero,12345,12345678,ebx,edi,NULL

	mov		ebx,pPointsR
	mov		edi,pWeightsR
	invoke	GenerateNeurons,NUM_CUBE_NEURONS,offset NeuronCentrePoints,123,1234568,ebx,edi,offset NeuronGraph
	add		ebx,NUM_CUBE_POINTS*sizeof VECTOR
	add		edi,NUM_CUBE_POINTS*4
	invoke	GenerateNeurons,1,offset AllZero,1234,123457,ebx,edi,NULL
	add		ebx,NUM_ORBITA_POINTS*sizeof VECTOR
	add		edi,NUM_ORBITA_POINTS*4
	invoke	GenerateNeurons,1,offset AllZero,12345,12345679,ebx,edi,NULL

	mov		ebx,pPointsS
	mov		edi,pWeightsS
	invoke	GenerateNeurons,NUM_CUBE_NEURONS,offset NeuronCentrePoints,123,1234569,ebx,edi,offset NeuronGraph
	add		ebx,NUM_CUBE_POINTS*sizeof VECTOR
	add		edi,NUM_CUBE_POINTS*4
	invoke	GenerateNeurons,1,offset AllZero,1234,123458,ebx,edi,NULL
	add		ebx,NUM_ORBITA_POINTS*sizeof VECTOR
	add		edi,NUM_ORBITA_POINTS*4
	invoke	GenerateNeurons,1,offset AllZero,12345,12345670,ebx,edi,NULL

	invoke	KillTimer,hWndGlobal,REDRAW_TIMER_ID
IF NOT TIMING
	invoke	SetTimer,hWndGlobal,REDRAW_TIMER_ID,REDRAW_PERIOD,NULL
ENDIF
IF SAVING_POINTS
	invoke	CreateFile,offset PointsFileName,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_ARCHIVE,NULL
	mov		hFile,eax
	invoke	WriteFile,hFile,pPointsQ,NUM_TOTAL_POINTS*10h,offset Dummy,NULL
	invoke	CloseHandle,hFile
	mov		PointsFileName[6],'R'
	invoke	CreateFile,offset PointsFileName,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_ARCHIVE,NULL
	mov		hFile,eax
	invoke	WriteFile,hFile,pPointsR,NUM_TOTAL_POINTS*10h,offset Dummy,NULL
	invoke	CloseHandle,hFile
	mov		PointsFileName[6],'S'
	invoke	CreateFile,offset PointsFileName,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_ARCHIVE,NULL
	mov		hFile,eax
	invoke	WriteFile,hFile,pPointsS,NUM_TOTAL_POINTS*10h,offset Dummy,NULL
	invoke	CloseHandle,hFile
	invoke	CreateFile,offset WeightsFileName,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_ARCHIVE,NULL
	mov		hFile,eax
	invoke	WriteFile,hFile,pWeightsQ,NUM_TOTAL_POINTS*4,offset Dummy,NULL
	invoke	CloseHandle,hFile
	mov		WeightsFileName[7],'R'
	invoke	CreateFile,offset WeightsFileName,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_ARCHIVE,NULL
	mov		hFile,eax
	invoke	WriteFile,hFile,pWeightsR,NUM_TOTAL_POINTS*4,offset Dummy,NULL
	invoke	CloseHandle,hFile
	mov		WeightsFileName[7],'S'
	invoke	CreateFile,offset WeightsFileName,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_ARCHIVE,NULL
	mov		hFile,eax
	invoke	WriteFile,hFile,pWeightsS,NUM_TOTAL_POINTS*4,offset Dummy,NULL
	invoke	CloseHandle,hFile
ENDIF

	mov		isDoneGenerating,TRUE
	ret
GenerationThread	endp

;>*******************************************************************************************************************************
;*																																*
;*	Function:	RecordTime																										*
;*																																*
;*																																*
;*	Parameters:																													*
;*	ecx	- The index of this datum in the timing data.																			*
;*																																*
;<*******************************************************************************************************************************
RecordTime	proc
IF TIMING
	cmp		nFrames,NUM_TIMING_FRAMES
	je		DoneTiming
	rdtsc
	push	eax
	push	edx
	sub		eax,dword ptr SavedTime
	sbb		edx,dword ptr SavedTime[4]
	mov		edx,0FFFFFFFFh
	cmovnz	eax,edx
	cmp		ecx,TIME_SAMPLES_PER_FRAME-1
	jne		NoSkipping
	mov		edx,dword ptr SavedTime
	or		edx,dword ptr SavedTime[4]
	jz		TimeNotYetStored
	mov		edx,nFrames
	add		nFrames,1
	jmp		AlreadyLoadedNumFrames
NoSkipping:
	mov		edx,nFrames
AlreadyLoadedNumFrames:
IF TIME_SAMPLES_PER_FRAME NE 8
	.err <Change the following line in RecordTime now that the size of all time samples in a frame is not 32 bytes.>
ENDIF
	shl		edx,5
	add		edx,pTimingData
	mov		[edx][ecx*4],eax
TimeNotYetStored:
	pop		dword ptr SavedTime[4]
	pop		dword ptr SavedTime
DoneTiming:
	ret
ENDIF
RecordTime	endp

;>*******************************************************************************************************************************
;*																																*
;*	Function:	SaveImage																										*
;*																																*
;*																																*
;<*******************************************************************************************************************************
SaveImage	proc
IF SAVING_IMAGES
	push	ebx
	push	esi
	invoke	CreateFile,offset ImageFileName,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_ARCHIVE,NULL
	mov		hFile,eax
	invoke	WriteFile,hFile,offset SavedBitmapFileHeader,sizeof BITMAPFILEHEADER+sizeof BITMAPINFOHEADER,offset Dummy,NULL
	mov		eax,pScreenPoints
	mov		ebx,pImageBuffer
	mov		esi,IMAGE_HEIGHT/4
CopyNextLine:
	xor		ecx,ecx
CopyNextPixel:
	mov		edx,[ebx+ecx]
	mov		[eax],edx
	add		ecx,4*4
	add		eax,3
	cmp		ecx,IMAGE_WIDTH*4
	jb		CopyNextPixel
	add		ebx,IMAGE_WIDTH*4*4
	sub		esi,1
	jnz		CopyNextLine
	invoke	WriteFile,hFile,pScreenPoints,(IMAGE_WIDTH/4)*(IMAGE_HEIGHT/4)*3,offset Dummy,NULL
	invoke	CloseHandle,hFile
	
	mov		al,ImageFileName[7]
	add		al,1
	mov		ImageFileName[7],al
	cmp		al,'9'
	jbe		DoneUpdateImageFilename
	sub		al,10
	mov		ImageFileName[7],al
	mov		al,ImageFileName[6]
	add		al,1
	mov		ImageFileName[6],al
	cmp		al,'9'
	jbe		DoneUpdateImageFilename
	sub		al,10
	mov		ImageFileName[6],al
	mov		al,ImageFileName[5]
	add		al,1
	mov		ImageFileName[5],al
DoneUpdateImageFilename:
	pop		esi
	pop		ebx
	ret
ENDIF
SaveImage	endp

;>*******************************************************************************************************************************
;*																																*
;*	Function:	ProcessCommandLine																								*
;*																																*
;*	Reads the command line and determines whether to run the screen saver normally, run it in the preview window, or show a		*
;*	settings dialog box																											*
;*																																*
;*	Returns:																													*
;*	eax	- one of the <Command Line Constants> or the handle of the preview window												*
;*																																*
;*	See Also:																													*
;*		- <Command Line Constants>																								*
;*																																*
;<*******************************************************************************************************************************
ProcessCommandLine	proc
	invoke	GetCommandLine
;The command line will start with the path of this program, which is not needed, so skip over it.
	mov		cl,[eax]

;If the command line starts with a quotation mark, skip until after the next quotation mark,
;since that's where the program name ends.
	cmp		cl,'"'
	jne		NotQuotationMark
FindQuotationMarkLoop:
	inc 	eax
	mov		cl,[eax]
	test	cl,cl					;Having the terminating zero before the next quotation mark is an error,
	jz		CommandLineError		;because the program name must end in a quotation mark if it started with one
	cmp		cl,'"'
	jne		FindQuotationMarkLoop
	jmp		SkipSpace

;If the command line doesn't start with a quotation mark, skip until the first space.
NotQuotationMark:
FindSpaceLoop:
	cmp		cl,' '
	je		SkipSpace
	test	cl,cl
	jz		NoCommandLineParameters
	inc		eax
	mov		cl,[eax]
	jmp		FindSpaceLoop

;Once the program name has been skipped, skip any space following it.
SkipSpace:
	inc		eax
	mov		cl,[eax]
	test	cl,cl
	jz		NoCommandLineParameters
	cmp		cl,' '
	je		SkipSpace

;At this point, there is a character that is not a space and not the terminating zero.
	cmp		cl,'/'
	je		CommandLineParameter
	cmp		cl,'-'
	jne		CommandLineError
;Now it's known that there is a parameter marked by / or -
CommandLineParameter:
	inc		eax
	mov		cl,[eax]
	or		cl,20h					;This makes any letters lowercase (41h-5Ah, 'AppName'-'Z', become 61h-7Ah, 'a'-'z'),
									;and doesn't change any numbers (30h-39h, '0'-'9', stay the same).
	cmp		cl,'c'
	je		ModalSettingsBox
	cmp		cl,'a'
	je		PasswordDialog
	cmp		cl,'p'
	je		RunInThePreviewWindow
	cmp		cl,'s'
CommandLineError:					;Just run the screensaver normally if there's an error in the command line.
RunTheScreenSaver:
	mov		eax,RUN_NORMALLY
	ret

RunInThePreviewWindow:
;What follows is some number of spaces and then the unsigned decimal text representation of the number that
;is the handle of the preview window.
SkipSpace2:
	inc		eax
	mov		cl,[eax]
	test	cl,cl					;If the command line ends here, there's a problem.
	jz		CommandLineError		;
	cmp		cl,' '
	je		SkipSpace2

	xor		edx,edx					;edx will hold the number being parsed
	movzx	ecx,cl					;ecx (cl) will be the current digit
DigitLoop:							;Together, the following two lines do: edx = edx*10 + (cl-'0')
	lea		edx,[edx*4+edx]			;	edx = edx*5
	lea		edx,[edx*2+ecx-'0']		;	edx = edx*2 + (cl-'0')
	inc		eax
	mov		cl,[eax]
	test	cl,cl
	jnz		DigitLoop
	mov		eax,edx					;return the window handle
	ret

PasswordDialog:
	mov		eax,SET_PASSWORD
	ret

ModalSettingsBox:
	mov		eax,MODAL_CONFIG
	ret

NoCommandLineParameters:
	mov		eax,MODELESS_CONFIG
	ret
ProcessCommandLine	endp


end main
